package proxy4jdbc3.jdbc;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Properties;

import proxy4jdbc3.JDBCProxy;

/**
 * A JDBC driver which is a facade that delegates to one real underlying JDBC
 * drivers. The driver will be like an proxy to the real JDBC driver instance
 * recovered by method to be implemented:
 * <code> public abstract Driver newDriverInstance(); </code>
 * 
 */
public abstract class DriverProxy implements Driver, JDBCProxy<Driver> {
	/**
	 * The real driver
	 */
	private Driver realDriver;
	
	/**
	 * Factory method to recover the real driver instance
	 * @return instance
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 * @throws SQLException 
	 */
	public Driver newDriverInstance() throws InstantiationException,
			IllegalAccessException, ClassNotFoundException, SQLException{
			//register the driver
			Class<Driver> clazz = (Class<Driver>) Class.forName(getDriverClassName());
			deregister(getDriverClassName());
			return clazz.newInstance();
	}

	/**
	 * Factory method to recover the vendor driver class
	 * @return string
	 */
	public abstract String getDriverClassName();
	
	
	/**
	 * Constructs the JDBCDriver JDBCProxy and call method
	 * <code> newDriverInstance();</code> to construct the instance of
	 * proxyfied driver. After register the JBDCDriverProxy class on
	 * java.sql.DriverManager.
	 * 
	 */
	public DriverProxy() {
		try {
			this.realDriver = newDriverInstance();

			if (this.realDriver == null)
				throw new Exception("Driver instance could not be null!");

		} catch (Exception s) {
			throw (RuntimeException) new RuntimeException(
					"Could not recover the instance of jdbc proxy driver!")
					.initCause(s);
		}
	}

	public static final void register(DriverProxy driverProxy) {
		try {
			DriverManager.registerDriver(driverProxy);
		} catch (SQLException s) {
			throw (RuntimeException) new RuntimeException(
					"Could not register jdbc proxy driver!").initCause(s);
		}

	}
	
	
	/**
	 * This deregisters an excisting driver
	 */
	static void deregister(String driverClass) throws SQLException {
		// Deregister the driver with DriverManager
		Enumeration<Driver> en= null;
		try {
			en = DriverManager.getDrivers();
			while (en.hasMoreElements()) {
				Driver d = (Driver) en.nextElement();
				if (d.getClass().getName().equals(driverClass)) {
					DriverManager.deregisterDriver(d);
				}
			}
		} catch (Exception e) {
			System.err.println("deregisterDriver() Failed:" + e);
		}
	}

	public Driver getProxyfied() {
		return realDriver;
	}

	public int getMajorVersion() {
		return getProxyfied().getMajorVersion();
	}

	public int getMinorVersion() {
		return getProxyfied().getMinorVersion();
	}

	public boolean jdbcCompliant() {
		return getProxyfied().jdbcCompliant();
	}

	public boolean acceptsURL(String url) throws SQLException {
		return getProxyfied().acceptsURL(url);
	}

	/**
	 * Connects through the ConnectionProxy
	 */
	public Connection connect(String url, Properties info) throws SQLException {
		return new ConnectionProxy(getReporter(), getProxyfied().connect(url, info));
	}

	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
			throws SQLException {
		return getProxyfied().getPropertyInfo(url, info);
	}
	
	
	

}
